package com.lt.common;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.media.Image;
import android.os.Build;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicYuvToRGB;
import android.view.View;

import androidx.annotation.RequiresApi;

import com.lt.base.BaseApplication;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;

public class ImageHelper
{

    private static ImageHelper instance;

    private ImageHelper()
    {
    }

    public static ImageHelper getInstance()
    {
        if (instance == null)
        {
            synchronized (ImageHelper.class)
            {
                instance = new ImageHelper();
            }
        }
        return instance;
    }

    /**
     * 缩放图片
     */
    public Bitmap zoomBitmap(Bitmap bitmap, int width, int height)
    {
        if (bitmap == null || (width < 1 && height < 1))
        {
            return null;
        }
        int oldwidth = bitmap.getWidth();
        int oldheight = bitmap.getHeight();
        float scaleWidth;
        float scaleHeight;
        // 计算缩放比例
        if (width < 1)
        {
            scaleWidth = scaleHeight = ((float) height / oldheight);
        } else if (height < 1)
        {
            scaleWidth = scaleHeight = ((float) width / oldwidth);
        } else
        {
            scaleWidth = ((float) width / oldwidth);
            scaleHeight = ((float) height / oldheight);
        }
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight); // 设置缩放比例
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, oldwidth, oldheight, matrix,
                true);
        return bitmap;
    }

    /**
     * 按宽度，等比缩放bitmap
     */
    public Bitmap zoomBitmap(Bitmap bitmap, int width)
    {
        return zoomBitmap(bitmap, width, 0);
    }

    /**
     * 缩放Drawable
     */
    public Drawable zoomDrawable(Drawable drawable, int w, int h)
    {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap oldbmp = ConvertHelper.getInstance().drawable2Bitmap(drawable); // drawable 转换成 bitmap
        Matrix matrix = new Matrix(); // 创建操作图片用的 Matrix 对象
        float scaleWidth = ((float) w / width); // 计算缩放比例
        float scaleHeight = ((float) h / height);
        matrix.postScale(scaleWidth, scaleHeight); // 设置缩放比例
        Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,
                matrix, true); // 建立新的 bitmap ，其内容是对原 bitmap 的缩放后的图
        return ConvertHelper.getInstance().bitmap2Drawable(BaseApplication.getContext().getResources(), newbmp); // 把 bitmap 转换成 drawable 并返回
    }

    public Drawable zoomDrawable(Drawable drawable, int w)
    {
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        Bitmap oldbmp = ConvertHelper.getInstance().drawable2Bitmap(drawable); // drawable 转换成 bitmap
        Matrix matrix = new Matrix(); // 创建操作图片用的 Matrix 对象
        float scaleWidth = ((float) w / width); // 计算缩放比例
        float scaleHeight = ((float) w / width);
        matrix.postScale(scaleWidth, scaleHeight); // 设置缩放比例
        Bitmap newbmp = Bitmap.createBitmap(oldbmp, 0, 0, width, height,
                matrix, true); // 建立新的 bitmap ，其内容是对原 bitmap 的缩放后的图
        return ConvertHelper.getInstance().bitmap2Drawable(BaseApplication.getContext().getResources(), newbmp); // 把 bitmap 转换成 drawable 并返回
    }

    /**
     * @return 将view转换成bitmap
     */
    public Bitmap view2Bitmap(View view)
    {
        view.destroyDrawingCache();
        view.measure(View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
                .makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.setDrawingCacheEnabled(true);
        return view.getDrawingCache(true);
    }

    public byte[] bmp2ByteArray(Bitmap bmp)
    {
        return bmp2ByteArray(bmp, true);
    }

    /**
     * @return 左右镜像
     */
    public Bitmap convertBmp(Bitmap bmp)
    {
        int w = bmp.getWidth();
        int h = bmp.getHeight();
        Bitmap convertBmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas cv = new Canvas(convertBmp);
        Matrix matrix = new Matrix();
//        m.postScale(1, -1); //镜像垂直翻转
        matrix.postScale(-1, 1); //镜像水平翻转
        Bitmap newBmp = Bitmap.createBitmap(bmp, 0, 0, w, h, matrix, true);
        cv.drawBitmap(newBmp, new Rect(0, 0, newBmp.getWidth(), newBmp.getHeight()), new Rect(0, 0, w, h), null);
        return convertBmp;
    }

    /**
     * @return convert Bitmap to byte array
     */
    public byte[] bmp2ByteArray(final Bitmap bmp, final boolean needRecycle)
    {
        if (bmp == null)
        {
            return null;
        }
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, output);
        if (needRecycle)
        {
            bmp.recycle();
        }
        byte[] result = output.toByteArray();
        try
        {
            output.close();
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * @return 将Bitmap按照新的宽度等比缩放。
     */
    public Bitmap getNewBitmapByWidth(Bitmap bitmap, int newWidth)
    {
        if (bitmap == null || newWidth < 1)
        {
            return null;
        }
        int oldWidth = bitmap.getWidth();
        int oldHeight = bitmap.getHeight();
        float scale = ((float) newWidth) / oldWidth;
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        return Bitmap.createBitmap(bitmap, 0, 0, oldWidth, oldHeight, matrix, true);
    }

    /**
     * @return 将Bitmap按照新的宽度等比缩放。
     */
    public Bitmap getNewBitmapByHeight(Bitmap bitmap, int newHeight)
    {
        if (bitmap == null || newHeight < 1)
        {
            return null;
        }
        int oldWidth = bitmap.getWidth();
        int oldHeight = bitmap.getHeight();
        float scale = ((float) newHeight) / oldHeight;
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        return Bitmap.createBitmap(bitmap, 0, 0, oldWidth, oldHeight, matrix, true);
    }

    /**
     * @param bitmap 传入Bitmap对象
     * @return 转换图片成圆形
     */
    public Bitmap toCircleBitmap(Bitmap bitmap)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float roundPx;
        float left = 0, top = 0, right = 0, bottom = 0, dst_left = 0, dst_top = 0, dst_right = 0, dst_bottom = 0;
        if (width <= height)
        {
            roundPx = width / 2;
            left = 0;
            top = 0;
            right = width;
            bottom = width;
            height = width;
            dst_left = 0;
            dst_top = 0;
            dst_right = width;
            dst_bottom = width;
        } else
        {
            roundPx = height / 2;
            float clip = (width - height) / 2;
            left = clip;
            right = width - clip;
            top = 0;
            bottom = height;
            width = height;
            dst_left = 0;
            dst_top = 0;
            dst_right = height;
            dst_bottom = height;
        }

        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom);
        final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom);
        final RectF rectF = new RectF(dst);

        paint.setAntiAlias(true);// 设置画笔无锯齿

        canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas
        paint.setColor(color);

        // 以下有两种方法画圆,drawRounRect和drawCircle
        // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形，第一个参数为图形显示区域，第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
        canvas.drawCircle(roundPx, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452
        canvas.drawBitmap(bitmap, src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已经draw了的Circle

        return output;
    }

    public int getBmpSize(Bitmap bmp)
    {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT)
        {
            return bmp.getByteCount();
        }
        return bmp.getRowBytes() * bmp.getHeight();
    }

    /**
     * 将bitmap保存到SD卡
     *
     * @param quality 图片质量，1为原图
     * @throws IOException 异常
     */
    public void saveBitmap(Bitmap bitmap, String path,
                           int quality) throws IOException
    {
        if (bitmap != null)
        {
            File folder = new File(path.substring(0,
                    path.lastIndexOf(File.separatorChar)));
            if (!folder.exists())
            {
                folder.mkdirs();
            }
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream(path));
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality,
                    bos);
            bos.flush();
            bos.close();
        }
    }


    /**
     * 质量压缩方法
     *
     * @param bitmap
     * @return
     */
    public Bitmap compressBitmap(Bitmap bitmap, long maxSize)
    {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法，这里100表示不压缩，把压缩后的数据存放到baos中
        int options = 90;
        while (baos.toByteArray().length > maxSize)
        { // 循环判断如果压缩后图片是否大于maxSize b,大于继续压缩
            baos.reset(); // 重置baos即清空baos
            bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%，把压缩后的数据存放到baos中
            options -= 10;// 每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
        return BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
    }

    /**
     * 旋转图片，使图片保持正确的方向。
     *
     * @param bitmap  原始图片
     * @param degrees 原始图片的角度
     * @return Bitmap 旋转后的图片
     */
    public Bitmap rotateBitmap(Bitmap bitmap, int degrees)
    {
        if (degrees == 0 || null == bitmap)
        {
            return bitmap;
        }
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Matrix matrix = new Matrix();
        matrix.setRotate(degrees);
        // 围绕原地进行旋转
        Bitmap newBM = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        if (newBM.equals(bitmap))
        {
            return newBM;
        }
        bitmap.recycle();
        return newBM;
    }

    public Bitmap bitMapScale(Bitmap bitmap, float scale)
    {
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale); //长和宽放大缩小的比例
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }

    /**
     * 裁剪
     *
     * @param bitmap 原图
     * @return 裁剪后的图像
     */
    private Bitmap cropBitmap(Bitmap bitmap)
    {
        int w = bitmap.getWidth(); // 得到图片的宽，高
        int h = bitmap.getHeight();
        int cropWidth = w >= h ? h : w;// 裁切后所取的正方形区域边长
        cropWidth /= 2;
        int cropHeight = (int) (cropWidth / 1.2);
        return Bitmap.createBitmap(bitmap, w / 3, 0, cropWidth, cropHeight, null, false);
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public Bitmap rgbaImageToBitmap(Image image)
    {
        if (image != null)
        {
            int width = image.getWidth();
            int height = image.getHeight();
            final Image.Plane[] planes = image.getPlanes();
            final ByteBuffer buffer = planes[0].getBuffer();
            int pixelStride = planes[0].getPixelStride();
            int rowStride = planes[0].getRowStride();
            int rowPadding = rowStride - pixelStride * width;
            Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(buffer);
            image.close();
            return bitmap;
        }
        return null;
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public Bitmap jpegImageToBitmap(Image image)
    {
        if (image != null)
        {
            ByteBuffer buffer = image.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            // 需要在这个事件监听里面调用close要不然会报上面的错误
            image.close();
            return ConvertHelper.getInstance().bytesToBitmap(bytes);
        }
        return null;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public Bitmap yuvImageToBitmap(Image image)
    {
        Bitmap bitmap = null;
        // Get the YUV data
        if (image != null)
        {
            final ByteBuffer yuvBytes = imageToByteBuffer(image);

            // Convert YUV to RGB

            final RenderScript rs = RenderScript.create(BaseApplication.getContext());

            bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
            final Allocation allocationRgb = Allocation.createFromBitmap(rs, bitmap);

            final Allocation allocationYuv = Allocation.createSized(rs, Element.U8(rs), yuvBytes.array().length);
            allocationYuv.copyFrom(yuvBytes.array());

            ScriptIntrinsicYuvToRGB scriptYuvToRgb = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
            scriptYuvToRgb.setInput(allocationYuv);
            scriptYuvToRgb.forEach(allocationRgb);

            allocationRgb.copyTo(bitmap);

            // Release
//            bitmap.recycle();

            allocationYuv.destroy();
            allocationRgb.destroy();
            rs.destroy();
            image.close();
        }
        return bitmap;
    }


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private ByteBuffer imageToByteBuffer(final Image image)
    {
        final Rect crop = image.getCropRect();
        final int width = crop.width();
        final int height = crop.height();
//        log("width:" + width + " height:" + height);
        final Image.Plane[] planes = image.getPlanes();
        final byte[] rowData = new byte[planes[0].getRowStride()];
        final int bufferSize = width * height * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8;
        final ByteBuffer output = ByteBuffer.allocateDirect(bufferSize);

        int channelOffset = 0;
        int outputStride = 0;

        for (int planeIndex = 0; planeIndex < 3; planeIndex++)
        {
            if (planeIndex == 0)
            {
                channelOffset = 0;
                outputStride = 1;
            } else if (planeIndex == 1)
            {
                channelOffset = width * height + 1;
                outputStride = 2;
            } else if (planeIndex == 2)
            {
                channelOffset = width * height;
                outputStride = 2;
            }

            final ByteBuffer buffer = planes[planeIndex].getBuffer();
            final int rowStride = planes[planeIndex].getRowStride();
            final int pixelStride = planes[planeIndex].getPixelStride();

            final int shift = (planeIndex == 0) ? 0 : 1;
            final int widthShifted = width >> shift;
            final int heightShifted = height >> shift;

            buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));

            for (int row = 0; row < heightShifted; row++)
            {
                final int length;

                if (pixelStride == 1 && outputStride == 1)
                {
                    length = widthShifted;
                    buffer.get(output.array(), channelOffset, length);
                    channelOffset += length;
                } else
                {
                    length = (widthShifted - 1) * pixelStride + 1;
                    buffer.get(rowData, 0, length);

                    for (int col = 0; col < widthShifted; col++)
                    {
                        output.array()[channelOffset] = rowData[col * pixelStride];
                        channelOffset += outputStride;
                    }
                }

                if (row < heightShifted - 1)
                {
                    buffer.position(buffer.position() + rowStride - length);
                }
            }
        }
        return output;
    }


    public byte[] YUV_420_888toNV21(Image image)
    {

        int width = image.getWidth();
        int height = image.getHeight();
        int ySize = width * height;
        int uvSize = width * height / 4;

        byte[] nv21 = new byte[ySize + uvSize * 2];

        ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y
        ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U
        ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V

        int rowStride = image.getPlanes()[0].getRowStride();
        assert (image.getPlanes()[0].getPixelStride() == 1);

        int pos = 0;

        if (rowStride == width)
        { // likely
            yBuffer.get(nv21, 0, ySize);
            pos += ySize;
        } else
        {
            int yBufferPos = -rowStride; // not an actual position
            for (; pos < ySize; pos += width)
            {
                yBufferPos += rowStride;
                yBuffer.position(yBufferPos);
                yBuffer.get(nv21, pos, width);
            }
        }

        rowStride = image.getPlanes()[2].getRowStride();
        int pixelStride = image.getPlanes()[2].getPixelStride();

        assert (rowStride == image.getPlanes()[1].getRowStride());
        assert (pixelStride == image.getPlanes()[1].getPixelStride());

        if (pixelStride == 2 && rowStride == width && uBuffer.get(0) == vBuffer.get(1))
        {
            // maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0]
            byte savePixel = vBuffer.get(1);
            try
            {
                vBuffer.put(1, (byte) ~savePixel);
                if (uBuffer.get(0) == (byte) ~savePixel)
                {
                    vBuffer.put(1, savePixel);
                    vBuffer.position(0);
                    uBuffer.position(0);
                    vBuffer.get(nv21, ySize, 1);
                    uBuffer.get(nv21, ySize + 1, uBuffer.remaining());

                    return nv21; // shortcut
                }
            } catch (ReadOnlyBufferException ex)
            {
                // unfortunately, we cannot check if vBuffer and uBuffer overlap
            }
            // unfortunately, the check failed. We must save U and V pixel by pixel
            vBuffer.put(1, savePixel);
        }

        // other optimizations could check if (pixelStride == 1) or (pixelStride == 2),
        // but performance gain would be less significant
        for (int row = 0; row < height / 2; row++)
        {
            for (int col = 0; col < width / 2; col++)
            {
                int vuPos = col * pixelStride + row * rowStride;
                nv21[pos++] = vBuffer.get(vuPos);
                nv21[pos++] = uBuffer.get(vuPos);
            }
        }
        return nv21;
    }
}
